/**
 *  @file enumDecl.h
 */
#include "../common/CppMemPool.h"
#include "../text/CFString.h"
#include "Primitive.h"
#include "../collections/HashtableT.h"

/*** undefine these first as they may have been defined previously ***/
#undef BEGIN_DECL_ENUM
#undef BEGIN_DECL_ENUM_NS1
#undef BEGIN_DECL_ENUM_NS2
#undef BEGIN_DECL_ENUM_NS3
#undef BEGIN_DECL_ENUM_NS4
#undef BEGIN_DECL_ENUM_NS5
#undef D_ENUM
#undef DV_ENUM
#undef END_DECL_ENUM
#undef END_DECL_ENUM_NS1
#undef END_DECL_ENUM_NS2
#undef END_DECL_ENUM_NS3
#undef END_DECL_ENUM_NS4
#undef END_DECL_ENUM_NS5

/*
 *  When GENERATE_ENUM_TABLE is defined, the 'GetEnumTable' function is defined and the 
 *  conversion table (using a Hashtable) is also created.
 *  Without GENERATE_ENUM_TABLE, normal declaration of enum is applied.
 *
 *  'GetEnumTable' is always defined under namespace cppflib::lang::Enum and is declarared one 
 *  for each enum type. 'GetEnumTable' is used by the template functions inside 'enumT.h'.
 *
 *  The conversion table is actually a hashtable using enum name as key and enum numeric value as
 *  the hash value. The table is initialized the first time it is requested.
 *
 *  Unlike 'GetEnumTable' itself, the enum can be declared under different namespace.
 *  
 *  BEGIN_DECL_ENUM has several versions which let you declare enum under global or several levels
 *  of namespace. At the same time, make sure to end the declaration using the matching END_DECL_ENUM.
 *  
 */


#ifdef GENERATE_ENUM_TABLE

#define BEGIN_DEFINE_FUNC(NAME) \
namespace cppflib { \
namespace lang { \
namespace Enum { \
    template <> \
    _CUSTOM_ENUMT_DLLAPI const cppflib::collections::HashtableT<cppflib::CFString, cppflib::lang::Int32> * GetEnumTable<NAME>(void) \
    { \
       static cppflib::collections::HashtableT<cppflib::CFString, cppflib::lang::Int32> * pEnumTbl = NULL; \
       if (pEnumTbl == NULL) { \
          cppflib::CFString *pKey; \
          cppflib::lang::Int32 *pVal; \
          int enumVal = 0; \
          pEnumTbl = new cppflib::collections::HashtableT<cppflib::CFString, cppflib::lang::Int32>(); \
          cppflib::cppmempool::UnlinkFromPool(pEnumTbl); \
          cppflib::cppmempool::AddToDefaultPool(pEnumTbl);

#define BEGIN_DECL_ENUM(NAME) BEGIN_DEFINE_FUNC(NAME)
#define BEGIN_DECL_ENUM_NS1(ns1, NAME) BEGIN_DEFINE_FUNC(ns1::NAME)
#define BEGIN_DECL_ENUM_NS2(ns1, ns2, NAME) BEGIN_DEFINE_FUNC(ns1::ns2::NAME)
#define BEGIN_DECL_ENUM_NS3(ns1, ns2, ns3, NAME) BEGIN_DEFINE_FUNC(ns1::ns2::ns3::NAME)
#define BEGIN_DECL_ENUM_NS4(ns1, ns2, ns3, ns4, NAME) BEGIN_DEFINE_FUNC(ns1::ns2::ns3::ns4::NAME)
#define BEGIN_DECL_ENUM_NS5(ns1, ns2, ns3, ns4, ns5, NAME) BEGIN_DEFINE_FUNC(ns1::ns2::ns3::ns4::ns5::NAME)

#define D_ENUM(ENUMNAME) \
          pKey = new cppflib::CFString(STRINGIZE(ENUMNAME)); \
          pVal = new cppflib::lang::Int32(enumVal++); \
          pEnumTbl->Set(pKey, pVal);

#define DV_ENUM(ENUMNAME, VAL) \
          pKey = new cppflib::CFString(STRINGIZE(ENUMNAME)); \
          enumVal = VAL; \
          pVal = new cppflib::lang::Int32(enumVal++); \
          pEnumTbl->Set(pKey, pVal);

#define END_DECL_ENUM \
       } \
       return pEnumTbl; \
    } \
} \
} \
}

#define END_DECL_ENUM_NS1 END_DECL_ENUM
#define END_DECL_ENUM_NS2 END_DECL_ENUM
#define END_DECL_ENUM_NS3 END_DECL_ENUM
#define END_DECL_ENUM_NS4 END_DECL_ENUM
#define END_DECL_ENUM_NS5 END_DECL_ENUM


#else

//! declare enum under a namespace
#define BEGIN_DECL_ENUM_NS1(ns1, NAME) \
namespace ns1 { \
   enum NAME {

//! declare enum under 2 level namespace
#define BEGIN_DECL_ENUM_NS2(ns1, ns2, NAME) \
namespace ns1 { \
namespace ns2 { \
   enum NAME {

//! declare enum under 3 level namespace
#define BEGIN_DECL_ENUM_NS3(ns1, ns2, ns3, NAME) \
namespace ns1 { \
namespace ns2 { \
namespace ns3 { \
   enum NAME {

//! declare enum under 4 level namespace
#define BEGIN_DECL_ENUM_NS4(ns1, ns2, ns3, ns4, NAME) \
namespace ns1 { \
namespace ns2 { \
namespace ns3 { \
namespace ns4 { \
   enum NAME {

//! declare enum under 5 level namespace
#define BEGIN_DECL_ENUM_NS5(ns1, ns2, ns3, ns4, ns5, NAME) \
namespace ns1 { \
namespace ns2 { \
namespace ns3 { \
namespace ns4 { \
namespace ns5 { \
   enum NAME {

//! declare enum under global namespace
#define BEGIN_DECL_ENUM(NAME) enum NAME {

//! declare enum using default value
#define D_ENUM(ENUMNAME) ENUMNAME,

//! declare enum with a particular value
#define DV_ENUM(ENUMNAME, VAL) ENUMNAME = VAL,

//! end of declaring under global namespace
#define END_DECL_ENUM };

//! end of declaring under 1 level namespace
#define END_DECL_ENUM_NS1 }; \
}

//! end of declaring under 2 level namespace
#define END_DECL_ENUM_NS2 }; \
} \
}

//! end of declaring under 3 level namespace
#define END_DECL_ENUM_NS3 }; \
} \
} \
}

//! end of declaring under 4 level namespace
#define END_DECL_ENUM_NS4 }; \
} \
} \
} \
}

//! end of declaring under 5 level namespace
#define END_DECL_ENUM_NS5 }; \
} \
} \
} \
} \
}

#endif
